package org.lex.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.net.SocketFactory;

import org.lex.io.IO;


public class TCPClient extends NetworkClient {
	private int connectTimeout = 0;
	private Socket socket = null;
	// ================================
	private InputStream serverInput = null;
	private OutputStream serverOutput = null;
	// ======================================
	private SocketFactory socketFactory = null;

	// =============================
	// Constructors
	// =============================
	public TCPClient() {
		this(SocketFactory.getDefault());
	}

	public TCPClient(SocketFactory factory) {
		soTimeout = 0;
		connectTimeout = 0;
		socket = null;
		socketFactory = factory;
	}

	// =============================
	// Instance Methods
	// =============================
	public void openServer(String host, int port) throws IOException, UnknownHostException {
		this.openServer(InetAddress.getByName(host), port);
	}

	@Override
	public void openServer(InetAddress host, int port) throws IOException {
		if (socket != null)
			closeServer();
		Socket s = socketFactory.createSocket();
		s.connect(new InetSocketAddress(host, port), connectTimeout);
		s.setSoTimeout(soTimeout);
		socket = s;
		serverOutput = socket.getOutputStream();
		serverInput = socket.getInputStream();
	}

	public void closeServer() throws IOException {
		if (!isServerOpen()) {
			return;
		} else {
			socket.close();
			socket = null;
			IO.closeQuietly(serverInput);
			IO.flushQuietly(serverOutput);
			IO.closeQuietly(serverOutput);
			serverInput = null;
			serverOutput = null;
			return;
		}
	}

	public boolean isServerOpen() {
		return socket != null;
	}

	public void setSoTimeout(int timeout) {
		if (timeout < 0)
			timeout = 0;
		if (socket != null)
			try {
				socket.setSoTimeout(timeout);
			} catch (IOException ioexception) {
			}
		soTimeout = timeout;
	}

	@Override
	public InetAddress getClientAddress() {
		if (socket == null)
			return null;
		else
			return socket.getLocalAddress();
	}

	@Override
	public int getClientPort() {
		return socket.getLocalPort();
	}

	@Override
	public InetAddress getServerAddress() {
		return socket.getInetAddress();
	}

	@Override
	public int getServerPort() {
		return socket.getPort();
	}

	public SocketFactory getSocketFactory() {
		return this.socketFactory;
	}

	public void setSocketFactory(SocketFactory factory) {
		if (null == factory)
			factory = SocketFactory.getDefault();
		this.socketFactory = factory;
	}

	public InputStream getServerInputStream() {
		return this.serverInput;
	}

	public OutputStream getServerOutputStream() {
		return this.serverOutput;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(int timeout) {
		connectTimeout = timeout;
	}

	// =============================
	// protected methods
	// =============================
	protected Socket getSocket() {
		return this.socket;
	}
}
